// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/filters/ffmpeg_aac_bitstream_converter.h"

#include "base/logging.h"
#include "media/ffmpeg/ffmpeg_common.h"

namespace media {

namespace {

    // Creates an ADTS header and stores in |hdr|
    // Assumes |hdr| points to an array of length |kAdtsHeaderSize|
    // Returns false if parameter values are for an unsupported configuration.
    bool GenerateAdtsHeader(int codec,
        int layer,
        int audio_profile,
        int sample_rate_index,
        int private_stream,
        int channel_configuration,
        int originality,
        int home,
        int copyrighted_stream,
        int copyright_start,
        int frame_length,
        int buffer_fullness,
        int number_of_frames_minus_one,
        uint8_t* hdr)
    {
        DCHECK_EQ(codec, AV_CODEC_ID_AAC);

        memset(reinterpret_cast<void*>(hdr), 0,
            FFmpegAACBitstreamConverter::kAdtsHeaderSize);
        // Ref: http://wiki.multimedia.cx/index.php?title=ADTS
        // ADTS header structure is the following
        // AAAAAAAA  AAAABCCD  EEFFFFGH  HHIJKLMM  MMMMMMMM  MMMOOOOO  OOOOOOPP
        //
        // A    Syncword 0xFFF, all bits must be 1
        // B    MPEG Version: 0 for MPEG-4, 1 for MPEG-2
        // C    Layer: always 0
        // D    Protection absent: Set to 1 if no CRC and 0 if there is CRC.
        // E    Profile: the MPEG-4 Audio Object Type minus 1.
        // F    MPEG-4 Sampling Frequency Index (15 is forbidden)
        // G    Private stream:
        // H    MPEG-4 Channel Configuration
        // I    Originality
        // J    Home
        // K    Copyrighted Stream
        // L    Copyright_ start
        // M    Frame length. This must include the ADTS header length.
        // O    Buffer fullness
        // P    Number of AAC frames in ADTS frame minus 1.
        //      For maximum compatibility always use 1 AAC frame per ADTS frame.

        // Syncword
        hdr[0] = 0xFF;
        hdr[1] = 0xF0;

        // Layer is always 0. No further action required.

        // Protection absent (no CRC) is always 1.
        hdr[1] |= 1;

        switch (audio_profile) {
        case FF_PROFILE_AAC_MAIN:
            break;
        case FF_PROFILE_AAC_HE:
        case FF_PROFILE_AAC_HE_V2:
        case FF_PROFILE_AAC_LOW:
            hdr[2] |= (1 << 6);
            break;
        case FF_PROFILE_AAC_SSR:
            hdr[2] |= (2 << 6);
            break;
        case FF_PROFILE_AAC_LTP:
            hdr[2] |= (3 << 6);
            break;
        default:
            DLOG(ERROR) << "[" << __func__ << "] "
                        << "unsupported audio profile:" << audio_profile;
            return false;
        }

        hdr[2] |= ((sample_rate_index & 0xf) << 2);

        if (private_stream)
            hdr[2] |= (1 << 1);

        switch (channel_configuration) {
        case 1:
            // front-center
            hdr[3] |= (1 << 6);
            break;
        case 2:
            // front-left, front-right
            hdr[3] |= (2 << 6);
            break;
        case 3:
            // front-center, front-left, front-right
            hdr[3] |= (3 << 6);
            break;
        case 4:
            // front-center, front-left, front-right, back-center
            hdr[2] |= 1;
            break;
        case 5:
            // front-center, front-left, front-right, back-left, back-right
            hdr[2] |= 1;
            hdr[3] |= (1 << 6);
            break;
        case 6:
            // front-center, front-left, front-right, back-left, back-right,
            // LFE-channel
            hdr[2] |= 1;
            hdr[3] |= (2 << 6);
            break;
        case 8:
            // front-center, front-left, front-right, side-left, side-right,
            // back-left, back-right, LFE-channel
            hdr[2] |= 1;
            hdr[3] |= (3 << 6);
            break;
        default:
            DLOG(ERROR) << "[" << __func__ << "] "
                        << "unsupported number of audio channels:"
                        << channel_configuration;
            return false;
        }

        if (originality)
            hdr[3] |= (1 << 5);

        if (home)
            hdr[3] |= (1 << 4);

        if (copyrighted_stream)
            hdr[3] |= (1 << 3);

        if (copyright_start)
            hdr[3] |= (1 << 2);

        // frame length
        hdr[3] |= (frame_length >> 11) & 0x03;
        hdr[4] = (frame_length >> 3) & 0xFF;
        hdr[5] |= (frame_length & 7) << 5;

        // buffer fullness
        hdr[5] |= (buffer_fullness >> 6) & 0x1F;
        hdr[6] |= (buffer_fullness & 0x3F) << 2;

        hdr[6] |= number_of_frames_minus_one & 0x3;

        return true;
    }

}

FFmpegAACBitstreamConverter::FFmpegAACBitstreamConverter(
    AVCodecContext* stream_codec_parameters)
    : stream_codec_parameters_(stream_codec_parameters)
    , header_generated_(false)
    , codec_()
    , audio_profile_()
    , sample_rate_index_()
    , channel_configuration_()
    , frame_length_()
{
    CHECK(stream_codec_parameters_);
}

FFmpegAACBitstreamConverter::~FFmpegAACBitstreamConverter()
{
}

bool FFmpegAACBitstreamConverter::ConvertPacket(AVPacket* packet)
{
    if (packet == NULL || !packet->data) {
        return false;
    }

    int header_plus_packet_size = packet->size + kAdtsHeaderSize;
    if (!stream_codec_parameters_->extradata) {
        DLOG(ERROR) << "extradata is null";
        return false;
    }
    if (stream_codec_parameters_->extradata_size < 2) {
        DLOG(ERROR) << "extradata too small to contain MP4A header";
        return false;
    }
    int sample_rate_index = ((stream_codec_parameters_->extradata[0] & 0x07) << 1) | ((stream_codec_parameters_->extradata[1] & 0x80) >> 7);
    if (sample_rate_index > 12) {
        sample_rate_index = 4;
    }

    if (!header_generated_ || codec_ != stream_codec_parameters_->codec_id || audio_profile_ != stream_codec_parameters_->profile || sample_rate_index_ != sample_rate_index || channel_configuration_ != stream_codec_parameters_->channels || frame_length_ != header_plus_packet_size) {
        header_generated_ = GenerateAdtsHeader(stream_codec_parameters_->codec_id,
            0, // layer
            stream_codec_parameters_->profile, sample_rate_index,
            0, // private stream
            stream_codec_parameters_->channels,
            0, // originality
            0, // home
            0, // copyrighted_stream
            0, // copyright_ start
            header_plus_packet_size,
            0x7FF, // buffer fullness
            0, // one frame per packet
            hdr_);
        codec_ = stream_codec_parameters_->codec_id;
        audio_profile_ = stream_codec_parameters_->profile;
        sample_rate_index_ = sample_rate_index;
        channel_configuration_ = stream_codec_parameters_->channels;
        frame_length_ = header_plus_packet_size;
    }

    // Inform caller if the header generation failed.
    if (!header_generated_)
        return false;

    // Allocate new packet for the output.
    AVPacket dest_packet;
    if (av_new_packet(&dest_packet, header_plus_packet_size) != 0)
        return false; // Memory allocation failure.

    memcpy(dest_packet.data, hdr_, kAdtsHeaderSize);
    memcpy(reinterpret_cast<void*>(dest_packet.data + kAdtsHeaderSize),
        reinterpret_cast<void*>(packet->data), packet->size);

    // This is a bit tricky: since the interface does not allow us to replace
    // the pointer of the old packet with a new one, we will initially copy the
    // metadata from old packet to new bigger packet.
    av_packet_copy_props(&dest_packet, packet);

    // Release the old packet.
    av_packet_unref(packet);
    *packet = dest_packet; // Finally, replace the values in the input packet.

    return true;
}

} // namespace media
